﻿// InputManager.cs
// Author: Gant Briseno
#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using BaseProjectGame.source.Engine;
#endregion

namespace BaseProjectGame.Engine
{
    #region Keyboard

    public enum ButtonPosition { Pressed = 0, Released, Down, Up }


    public static class CKeyboardManager
    {
        static KeyboardState keyboardState;
        static KeyboardState prevKeyboardState;
        static List<Binding> currBinding;
        static List<BindingSet> bindingSets;

        /// <summary>
        /// Sets up everything
        /// </summary>
        public static void Initialize()
        {
            currBinding = new List<Binding>();
            bindingSets = new List<BindingSet>();

            prevKeyboardState = keyboardState = Keyboard.GetState();
        }

        public static void Update(float dt)
        {
            prevKeyboardState = keyboardState;
            keyboardState = Keyboard.GetState();

            foreach (Binding bind in currBinding)
            {
                switch (bind.position)
                {
                    case ButtonPosition.Down:
                        if (KeyDown(bind.key))
                            CEventSystem.BroadcastEvent(bind.eventID);
                        break;
                    case ButtonPosition.Pressed:
                        if (KeyPressed(bind.key))
                            CEventSystem.BroadcastEvent(bind.eventID);
                        break;
                    case ButtonPosition.Released:
                        if (KeyReleased(bind.key))
                            CEventSystem.BroadcastEvent(bind.eventID);
                        break;
                    case ButtonPosition.Up:
                        if (KeyReleased(bind.key))
                            CEventSystem.BroadcastEvent(bind.eventID);
                        break;
                }
            }
        }

        /// <summary>
        /// Used to bind a specific action to a specific key
        /// </summary>
        private struct Binding
        {
            public Keys key { get; set; }
            public ButtonPosition position { get; set; }
            public EventID eventID { get; set; }
        }

        /// <summary>
        /// Holds a collection of Binding objects
        /// </summary>
        private class BindingSet
        {
            public List<Binding> bindings { get; set; }
            public string name { get; set; }

            public BindingSet()
            {
                bindings = new List<Binding>();
                name = "";
            }
        }

        /// <summary>
        /// Clears out the current binding
        /// </summary>
        public static void ClearCurrentBindings()
        {
            currBinding.Clear();
        }

        /// <summary>
        /// Clears all binding sets
        /// </summary>
        public static void ClearAllBindings()
        {
            currBinding.Clear();
            bindingSets.Clear();
        }

        /// <summary>
        /// Adds a new binding to the current set
        /// </summary>
        /// <param name="key">Key to check</param>
        /// <param name="position">Position the key needs to be in</param>
        /// <param name="eventID">Event bound to key</param>
        public static void AddBinding(Keys key, ButtonPosition position, EventID eventID)
        {
            Binding newBind = new Binding();
            newBind.eventID = eventID;
            newBind.key = key;
            newBind.position = position;
            currBinding.Add(newBind);
        }

        /// <summary>
        /// Removes the first binding it find that matches the params
        /// </summary>
        /// <param name="key">Key to check</param>
        /// <param name="position">Position of the key</param>
        /// <returns>Whether it removed a binding or not</returns>
        public static bool RemoveBinding(Keys key, ButtonPosition position)
        {
            for(int i = 0; i < currBinding.Count; ++i)
                if (currBinding[i].key == key && currBinding[i].position == position)
                {
                    currBinding.RemoveAt(i);
                    return true;
                }

            return false;
        }

        /// <summary>
        /// Stores the current binding in the BindingSet List
        /// </summary>
        /// <param name="name">Name of the current binding</param>
        /// <returns></returns>
        public static bool SaveCurrentBindingAs(string name)
        {
            name = name.ToLower();
            for (int i = 0; i < bindingSets.Count; ++i)
                if (name == bindingSets[i].name)
                    return false;

            BindingSet set = new BindingSet();
            set.name = name;
            set.bindings = new List<Binding>();
            foreach (Binding bind in currBinding)
                set.bindings.Add(bind);

            return true;
        }

        /// <summary>
        /// Loads a binding from the List
        /// </summary>
        /// <param name="name">Name of the set to use</param>
        /// <returns>Whether it found and loaded the binding or not</returns>
        public static bool LoadBinding(string name)
        {
            name = name.ToLower();
            for(int i = 0; i < bindingSets.Count; ++i)
                if (bindingSets[i].name == name)
                {
                    currBinding.Clear();
                    foreach (Binding bind in bindingSets[i].bindings)
                        currBinding.Add(bind);

                    return true;
                }

            return false;
        }

        /// <summary>
        /// Gets all keys that have been pressed this pass
        /// </summary>
        /// <returns>An array of pressed keys</returns>
        public static Keys[] GetPressedKeys()
        {
            Keys[] keyArr = new Keys[keyboardState.GetPressedKeys().Length];
            for (int i = 0; i < keyboardState.GetPressedKeys().Length; ++i)
                keyArr[i] = keyboardState.GetPressedKeys()[i];

            return keyArr;    
        }

        /// <summary>
        /// Checks to see if the key is being pressed this pass
        /// </summary>
        /// <param name="checkKey">Key to check</param>
        /// <returns></returns>
        public static bool KeyPressed(Keys checkKey)
        {
            return keyboardState.IsKeyDown(checkKey) && prevKeyboardState.IsKeyUp(checkKey);
        }

        /// <summary>
        /// Checks to see if the key is being released this pass
        /// </summary>
        /// <param name="checkKey">Key to check</param>
        /// <returns></returns>
        public static bool KeyReleased(Keys checkKey)
        {
            return keyboardState.IsKeyUp(checkKey) && prevKeyboardState.IsKeyDown(checkKey);
        }

        /// <summary>
        /// Checks to see if the key is up and was up last pass
        /// </summary>
        /// <param name="checkKey">Key to check</param>
        /// <returns></returns>
        public static bool KeyUp(Keys checkKey)
        {
            return keyboardState.IsKeyUp(checkKey) && prevKeyboardState.IsKeyUp(checkKey);
        }

        /// <summary>
        /// Checks to see if the key is being held down
        /// (down this pass and last pass)
        /// </summary>
        /// <param name="checkKey">Key to check</param>
        /// <returns></returns>
        public static bool KeyDown(Keys checkKey)
        {
            return keyboardState.IsKeyDown(checkKey) && prevKeyboardState.IsKeyDown(checkKey);
        }
    }
#endregion

#region Mouse

    public enum MouseButtons { LeftButton, MiddleButton, RightButton, XButton1, XButton2 }
    public enum MouseWheelDirection { None, Away, Toward, Any }
    public enum MouseDirection { None, Up, Down, Left, Right, Any }

    public static class CMouseManager
    {
        static MouseState mouseState;
        static MouseState prevMouseState;
        static List<Binding> currBindings;
        static List<BindingSet> bindingSets;
        static float invert;

        /// <summary>
        /// Set up some default values
        /// </summary>
        public static void Initialize()
        {
            currBindings = new List<Binding>();
            bindingSets = new List<BindingSet>();
            invert = 1f;

            prevMouseState = mouseState = Mouse.GetState();
        }

        /// <summary>
        /// Updates the MouseState and fires off any events
        /// </summary>
        /// <param name="dt">Delta Time</param>
        public static void Update(float dt)
        {
            prevMouseState = mouseState;
            mouseState = Mouse.GetState();

            foreach (Binding bind in currBindings)
            {
                switch (bind.type)
                {
                    case Binding.BindingType.Button:
                        switch (bind.position)
                        {
                            case ButtonPosition.Down:
                                if (ButtonDown(bind.button))
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case ButtonPosition.Pressed:
                                if (ButtonPressed(bind.button))
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case ButtonPosition.Released:
                                if (ButtonReleased(bind.button))
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case ButtonPosition.Up:
                                if (ButtonUp(bind.button))
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                        }
                        break;
                    case Binding.BindingType.Position:
                        switch (bind.direction)
                        {
                            case MouseDirection.Any:
                                if (MouseTravel() != Vector2.Zero)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseDirection.Down:
                                if (MouseTravel().Y < 0f)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseDirection.Left:
                                if (MouseTravel().X < 0f)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseDirection.None:
                                if (MouseTravel() == Vector2.Zero)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseDirection.Right:
                                if (MouseTravel().X > 0f)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseDirection.Up:
                                if (MouseTravel().Y > 0f)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                        }
                        break;
                    case Binding.BindingType.Wheel:
                        switch (bind.wheelDirection)
                        {
                            case MouseWheelDirection.Any:
                                if (ScrollWheelValue() != 0)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseWheelDirection.Away:
                                if (ScrollWheelValue() > 0)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseWheelDirection.None:
                                if (ScrollWheelValue() == 0)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                            case MouseWheelDirection.Toward:
                                if (ScrollWheelValue() < 0)
                                    CEventSystem.BroadcastEvent(bind.eventID, MousePosition());
                                break;
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// Holds information bound to a single event
        /// </summary>
        private struct Binding
        {
            public enum BindingType { Button, Position, Wheel }
            public BindingType type { get; set; }
            public MouseButtons button { get; set; }
            public ButtonPosition position { get; set; }
            public MouseDirection direction { get; set; }
            public MouseWheelDirection wheelDirection { get; set; }
            public EventID eventID { get; set; }
        }

        /// <summary>
        /// Holds a collection of Bindings
        /// </summary>
        private class BindingSet
        {
            public List<Binding> bindings { get; set; }
            public string name { get; set; }

            public BindingSet()
            {
                bindings = new List<Binding>();
                name = "";
            }
        }

        /// <summary>
        /// Clears the current binding
        /// </summary>
        public static void ClearCurrentBindings()
        {
            currBindings.Clear();
        }

        /// <summary>
        /// Clears both the current binding and stored bindings
        /// </summary>
        public static void ClearAllBindings()
        {
            currBindings.Clear();
            bindingSets.Clear();
        }

        /// <summary>
        /// Adds a new bind to the current binding
        /// </summary>
        /// <param name="button">Button to check</param>
        /// <param name="position">Position to activate event</param>
        /// <param name="eventID">Event to fire off</param>
        public static void AddBinding(MouseButtons button, ButtonPosition position, EventID eventID)
        {
            Binding newBind = new Binding();
            newBind.type = Binding.BindingType.Button;
            newBind.button = button;
            newBind.position = position;
            newBind.eventID = eventID;
            currBindings.Add(newBind);
        }

        /// <summary>
        /// Adds a new bind to the current binding
        /// </summary>
        /// <param name="direction">Direction to check</param>
        /// <param name="eventID">Event to fire off</param>
        public static void AddBinding(MouseDirection direction, EventID eventID)
        {
            Binding newBind = new Binding();
            newBind.type = Binding.BindingType.Position;
            newBind.direction = direction;
            newBind.eventID = eventID;
            currBindings.Add(newBind);
        }

        /// <summary>
        /// Adds a new bind to the current binding
        /// </summary>
        /// <param name="wheelDirection">Direction to check</param>
        /// <param name="eventID">Event to fire off</param>
        public static void AddBinding(MouseWheelDirection wheelDirection, EventID eventID)
        {
            Binding newBind = new Binding();
            newBind.type = Binding.BindingType.Wheel;
            newBind.wheelDirection = wheelDirection;
            newBind.eventID = eventID;
            currBindings.Add(newBind);
        }

        /// <summary>
        /// Removes a bind from the current binding
        /// </summary>
        /// <param name="button">Button to check</param>
        /// <param name="position">Position to check</param>
        /// <returns>Whether it found and removed the binding or not</returns>
        public static bool RemoveBinding(MouseButtons button, ButtonPosition position)
        {
            for (int i = 0; i < currBindings.Count; ++i)
                if (currBindings[i].button == button && currBindings[i].position == position)
                {
                    currBindings.RemoveAt(i);
                    return true;
                }

            return false;
        }

        /// <summary>
        /// Removes a bind from the current binding
        /// </summary>
        /// <param name="direction">Direction to check</param>
        /// <returns>Whether it found and removed the binding or not</returns>
        public static bool RemoveBinding(MouseDirection direction)
        {
            for (int i = 0; i < currBindings.Count; ++i)
                if (currBindings[i].direction == direction)
                {
                    currBindings.RemoveAt(i);
                    return true;
                }

            return false;
        }

        /// <summary>
        /// Removes a bind from the current binding
        /// </summary>
        /// <param name="wheelDirection">Direction to check</param>
        /// <returns>Whether it found and removed the binding or not</returns>
        public static bool RemoveBinding(MouseWheelDirection wheelDirection)
        {
            for(int i = 0; i < currBindings.Count; ++i)
                if (currBindings[i].wheelDirection == wheelDirection)
                {
                    currBindings.RemoveAt(i);
                    return true;
                }

            return false;
        }

        /// <summary>
        /// Saves the current bindings in the bindingSets list
        /// </summary>
        /// <param name="name">Name of the binding set</param>
        /// <returns>False if the name was already taken</returns>
        public static bool SaveCurrentBindingAs(string name)
        {
            name = name.ToLower();

            for (int i = 0; i < bindingSets.Count; ++i)
                if (bindingSets[i].name == name)
                    return false;

            BindingSet newSet = new BindingSet();
            newSet.name = name;
            newSet.bindings = new List<Binding>();
            foreach (Binding bind in currBindings)
                newSet.bindings.Add(bind);

            return true;
        }
        /// <summary>
        /// Loads a binding set from the saved set
        /// </summary>
        /// <param name="name">Name of the binding set</param>
        /// <returns>Whether it found and loaded the binding set or not</returns>
        public static bool LoadBinding(string name)
        {
            name = name.ToLower();
            for (int i = 0; i < bindingSets.Count; ++i)
            {
                if (bindingSets[i].name == name)
                {
                    currBindings.Clear();
                    foreach (Binding bind in bindingSets[i].bindings)
                        currBindings.Add(bind);
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Gets the mouse position
        /// With camera offset
        /// </summary>
        /// <returns></returns>
        public static Vector2 MousePosition()
        {
            Vector2 temp = new Vector2((float)mouseState.X, (float)mouseState.Y);
            return temp;// +CGame.GetInstance().m_Camera.m_vPosition;
        }

        /// <summary>
        /// Gets the travel of the mouse since last pass
        /// Supports Y inversion
        /// </summary>
        /// <returns></returns>
        public static Vector2 MouseTravel()
        {
            return new Vector2((float)(mouseState.X - prevMouseState.X), (float)(mouseState.Y - prevMouseState.Y) * invert);
        }

        /// <summary>
        /// Gets the travel of the wheel since the last pass
        /// </summary>
        /// <returns></returns>
        public static int ScrollWheelValue()
        {
            return mouseState.ScrollWheelValue;
        }

        /// <summary>
        /// Gets the direction of the wheel since the last pass
        /// </summary>
        /// <returns></returns>
        public static MouseWheelDirection FindScrollWheelDirection()
        {
            if (mouseState.ScrollWheelValue == 0)
                return MouseWheelDirection.None;
            else if (mouseState.ScrollWheelValue > 0)
                return MouseWheelDirection.Away;
            else
                return MouseWheelDirection.Toward;
        }

        /// <summary>
        /// Checks input to see if the button was pressed this pass
        /// </summary>
        /// <param name="check">Button to check</param>
        /// <returns></returns>
        public static bool ButtonPressed(MouseButtons check)
        {
            switch (check)
            {
                case MouseButtons.LeftButton:
                    return mouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton == ButtonState.Released;
                case MouseButtons.MiddleButton:
                    return mouseState.MiddleButton == ButtonState.Pressed && prevMouseState.MiddleButton == ButtonState.Released;
                case MouseButtons.RightButton:
                    return mouseState.RightButton == ButtonState.Pressed && prevMouseState.RightButton == ButtonState.Released;
                case MouseButtons.XButton1:
                    return mouseState.XButton1 == ButtonState.Pressed && prevMouseState.XButton1 == ButtonState.Released;
                case MouseButtons.XButton2:
                    return mouseState.XButton2 == ButtonState.Pressed && prevMouseState.XButton2 == ButtonState.Released;
            }

            return false;
        }

        /// <summary>
        /// Checks input to see if the button was released this pass
        /// </summary>
        /// <param name="check">Button to check</param>
        /// <returns></returns>
        public static bool ButtonReleased(MouseButtons check)
        {
            switch (check)
            {
                case MouseButtons.LeftButton:
                    return mouseState.LeftButton == ButtonState.Released && prevMouseState.LeftButton == ButtonState.Pressed;
                case MouseButtons.MiddleButton:
                    return mouseState.MiddleButton == ButtonState.Released && prevMouseState.MiddleButton == ButtonState.Pressed;
                case MouseButtons.RightButton:
                    return mouseState.RightButton == ButtonState.Released && prevMouseState.RightButton == ButtonState.Pressed;
                case MouseButtons.XButton1:
                    return mouseState.XButton1 == ButtonState.Released && prevMouseState.XButton1 == ButtonState.Pressed;
                case MouseButtons.XButton2:
                    return mouseState.XButton2 == ButtonState.Released && prevMouseState.XButton2 == ButtonState.Pressed;
            }

            return false;
        }

        /// <summary>
        /// Checks to see if the button is being held down
        /// </summary>
        /// <param name="check">Button to check</param>
        /// <returns></returns>
        public static bool ButtonDown(MouseButtons check)
        {
            switch (check)
            {
                case MouseButtons.LeftButton:
                    return mouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton == ButtonState.Pressed;
                case MouseButtons.MiddleButton:
                    return mouseState.MiddleButton == ButtonState.Pressed && prevMouseState.MiddleButton == ButtonState.Pressed;
                case MouseButtons.RightButton:
                    return mouseState.RightButton == ButtonState.Pressed && prevMouseState.RightButton == ButtonState.Pressed;
                case MouseButtons.XButton1:
                    return mouseState.XButton1 == ButtonState.Pressed && prevMouseState.XButton1 == ButtonState.Pressed;
                case MouseButtons.XButton2:
                    return mouseState.XButton2 == ButtonState.Pressed && prevMouseState.XButton2 == ButtonState.Pressed;
            }

            return false;
        }

        /// <summary>
        /// Checks to see if a button is idle
        /// </summary>
        /// <param name="check">Button to check</param>
        /// <returns></returns>
        public static bool ButtonUp(MouseButtons check)
        {
            switch (check)
            {
                case MouseButtons.LeftButton:
                    return mouseState.LeftButton == ButtonState.Released && prevMouseState.LeftButton == ButtonState.Released;
                case MouseButtons.MiddleButton:
                    return mouseState.MiddleButton == ButtonState.Released && prevMouseState.MiddleButton == ButtonState.Released;
                case MouseButtons.RightButton:
                    return mouseState.RightButton == ButtonState.Released && prevMouseState.RightButton == ButtonState.Released;
                case MouseButtons.XButton1:
                    return mouseState.XButton1 == ButtonState.Released && prevMouseState.XButton1 == ButtonState.Released;
                case MouseButtons.XButton2:
                    return mouseState.XButton2 == ButtonState.Released && prevMouseState.XButton2 == ButtonState.Released;
            }

            return false;
        }
    }

#endregion
}
